-
Notifications
You must be signed in to change notification settings - Fork 16
Implement caching types #26
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i find the name excluded_directives
confusing. we seem to not exclude those directives, but use them.
i would call the option respect_response_cache_directives
and deprecate respect_cache_headers
.
src/CachePlugin.php
Outdated
public static function clientCache(CacheItemPoolInterface $pool, StreamFactory $streamFactory, array $config = []) | ||
{ | ||
// Allow caching of private requests | ||
$config['excluded_directives'] = ['no-cache']; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should check if the option already exists and add it to the existing list
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or maybe just allow respect_cache_headers
to be either boolean or array. If boolean true then assume it is all directives. If false then assume it is empty array.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the things like no-cache are not headers, but directives. and some can be both on request and/or response, and we should treat that separately to allow the user full control. maybe they want no-cache on request to be respected, but on the response they still want to cache...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm also more in favor of separate options. Because I would avoid mixing option types bool/array
@dbu thanks for the feedback! I've updated the code! |
I've tested my patch on a project using the github api (which sends the private cache directive with each request). And I can confirm it works when you construct the cache plugin with the clientCache method. The phpspec tests fail after the patch, but I will look into this fail later tonight! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks, i think we move in the right direction with this. this will end up being quite flexible, while still being reasonably understandable.
src/CachePlugin.php
Outdated
@@ -242,7 +268,7 @@ private function createCacheKey(RequestInterface $request) | |||
*/ | |||
private function getMaxAge(ResponseInterface $response) | |||
{ | |||
if (!$this->config['respect_cache_headers']) { | |||
if (!$this->config['respect_cache_headers'] || !in_array('max-age', $this->config['respect_response_cache_directives'], true)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i would do the cleanup (create an empty array in respect_response_cache_directives) of this in the constructor and issue a deprecation warning if respect_cache_headers is defined. if both are defined in the constructor, throw an exception as its wrong to do that.
src/CachePlugin.php
Outdated
return false; | ||
|
||
foreach ($this->config['respect_response_cache_directives'] as $cacheDirective) { | ||
if ($this->getCacheControlDirective($response, $cacheDirective)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this means that if max-age is set to be respected and defined, we do not cache. i think you need a constant for NO_CACHE_FLAGS and array intersect that with respect_response_cache_directives
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes you are right, I will look into a way to do this. We can't use a constant with an array value as the cacheplugin supports php >=5.4
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah, right. then private property is fine too
src/CachePlugin.php
Outdated
@@ -58,6 +59,28 @@ public function __construct(CacheItemPoolInterface $pool, StreamFactory $streamF | |||
$this->config = $optionsResolver->resolve($config); | |||
} | |||
|
|||
public static function clientCache(CacheItemPoolInterface $pool, StreamFactory $streamFactory, array $config = []) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lets add a phpdoc here that says this sets up the cache plugin in a client mode. it will cache even with cache-control private or no-store.
src/CachePlugin.php
Outdated
'respect_cache_headers' => true, | ||
'hash_algo' => 'sha1', | ||
'methods' => ['GET', 'HEAD'], | ||
'respect_response_cache_directives' => ['no-cache', 'private', 'max-age'], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to stay BC, this should include no-store
@dbu I've incorporated the feedback and I've added a phpspec test for this new option/behavior |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for this PR. I really like the idea of these factory methods.
I had some comments on some things I did not understand.
src/CachePlugin.php
Outdated
@@ -34,31 +35,83 @@ | |||
private $config; | |||
|
|||
/** | |||
* Cache directives indicating if a response can be cached. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"can be cached"? "noCacheFlags"?
You mean cannot be cached?
src/CachePlugin.php
Outdated
* } | ||
*/ | ||
public function __construct(CacheItemPoolInterface $pool, StreamFactory $streamFactory, array $config = []) | ||
{ | ||
$this->pool = $pool; | ||
$this->streamFactory = $streamFactory; | ||
|
||
if (isset($config['respect_cache_headers']) && $config['respect_response_cache_directives']) { | ||
throw new \InvalidArgumentException('You can\'t provide config option "respect_cache_headers" and "respect_response_cache_directives". Use "respect_response_cache_directives" instead.'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally. Make lines shorter than 100 chars. (PSR-2)
src/CachePlugin.php
Outdated
$optionsResolver = new OptionsResolver(); | ||
$this->configureOptions($optionsResolver); | ||
$this->config = $optionsResolver->resolve($config); | ||
} | ||
|
||
/** | ||
* This method will setup the cachePlugin in client cache mode. When using the client cache mode the plugin will cache responses with `private` cache directive. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here.. Long lines.
src/CachePlugin.php
Outdated
* | ||
* @param CacheItemPoolInterface $pool | ||
* @param StreamFactory $streamFactory | ||
* @param array $config |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"See config for constructor" or something similar.
src/CachePlugin.php
Outdated
} | ||
|
||
/** | ||
* This method will setup the cachePlugin in server cache mode. This is the default caching behavior (refuses to cache responses with the `private`or `no-cache` directives. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing space
src/CachePlugin.php
Outdated
// Allow caching of private requests | ||
if (isset($config['respect_response_cache_directives'])) { | ||
if (!in_array('no-cache', $config['respect_response_cache_directives'], true)) { | ||
$config['respect_response_cache_directives'][] = ['no-cache']; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should you add an array here? ['no-cache']
vs 'no-cache'
.
src/CachePlugin.php
Outdated
* | ||
* @param CacheItemPoolInterface $pool | ||
* @param StreamFactory $streamFactory | ||
* @param array $config |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here, document what parameters or refer to constructor
@@ -183,11 +236,12 @@ protected function isCacheable(ResponseInterface $response) | |||
if (!in_array($response->getStatusCode(), [200, 203, 300, 301, 302, 404, 410])) { | |||
return false; | |||
} | |||
if (!$this->config['respect_cache_headers']) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This cannot be removed. That will break BC.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On line 361 I've added some BC logic. If this option is false we add an empty array so all the checks are skipped and original behavior is kept
if (!$this->config['respect_cache_headers']) { | ||
return true; | ||
} | ||
if ($this->getCacheControlDirective($response, 'no-store') || $this->getCacheControlDirective($response, 'private')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removing this will also break BC, right?
Can we do something smart in the constructor? If respect_cache_headers
is true then populate respect_response_cache_directives
with "no-store", "private".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this breaks BC as the default options include the headers
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agree with acrobat, this is handled below with the directives
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good
src/CachePlugin.php
Outdated
if ($this->getCacheControlDirective($response, 'no-store') || $this->getCacheControlDirective($response, 'private')) { | ||
return false; | ||
|
||
$cacheableDirectives = $this->extractDirectives($this->config['respect_response_cache_directives'], $this->noCacheFlags); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$cacheableDirectives can only be one of these four (no matter what). Is this really the intention?
['no-cache', 'private']
['private']
['no-cache']
[]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The directive list contains other values like max-age, no-store
and possibly other values in the future. So I've added this method to easily extract cache directives if they are provided in the config
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i agree with the approach. but i would directly use array_intersect here. as you assign it to a variable, its clear enough without the method. i would call the variable $nocacheDirectives.
Thanks @Nyholm for the review! I've fixed your comments |
src/CachePlugin.php
Outdated
if (isset($config['respect_response_cache_directives'])) { | ||
$config['respect_response_cache_directives'][] = 'no-cache'; | ||
$config['respect_response_cache_directives'][] = 'max-age'; | ||
array_unique($config['respect_response_cache_directives']); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
afaik array_unique does not modify the input array. you need to re-assign the value to the field.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes you're right! Fixed!
src/CachePlugin.php
Outdated
if ($this->getCacheControlDirective($response, 'no-store') || $this->getCacheControlDirective($response, 'private')) { | ||
return false; | ||
|
||
$cacheableDirectives = $this->extractDirectives($this->config['respect_response_cache_directives'], $this->noCacheFlags); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i agree with the approach. but i would directly use array_intersect here. as you assign it to a variable, its clear enough without the method. i would call the variable $nocacheDirectives.
if (!$this->config['respect_cache_headers']) { | ||
return true; | ||
} | ||
if ($this->getCacheControlDirective($response, 'no-store') || $this->getCacheControlDirective($response, 'private')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agree with acrobat, this is handled below with the directives
@dbu Fixed your new comments! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we are almost there i think. no-cache was not handled previously, but i consider that a bugfix rather than a BC break.
src/CachePlugin.php
Outdated
* we have to store the cache for a longer time than the server originally says it is valid for. | ||
* We store a cache item for $cache_lifetime + max age of the response. | ||
* @var array $methods list of request methods which can be cached | ||
* @var array $respect_response_cache_directives list of cache directives this plugin will respect while caching responses. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this was indented on purpose, because its the list of fields in the $config array.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I think this was fix for styleCi but indeed I've reverted the indent changes!
src/CachePlugin.php
Outdated
* We store a cache item for $cache_lifetime + max age of the response. | ||
* @var array $methods list of request methods which can be cached | ||
* @var array $respect_response_cache_directives list of cache directives this plugin will respect while caching responses. | ||
* }. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and this was left becaues it ends the list of options
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See comment above
src/CachePlugin.php
Outdated
* | ||
* @var array | ||
*/ | ||
private $noCacheFlags = ['no-cache', 'private']; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think this list should also have no-store
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAIK you can actually cache the result if response has no-cache
directive. It just means you must always revalidate before actually using the cached result. If origin server returns 304 Not Modified
you can use the cached result.
That said the easy implementation is just not to cache. Only difference is some bandwith might be wasted, but implementation is much easier.
https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
right. lets open an issue to implement the full no-cache behaviour in the future. but rather than completely ignoring, completely not caching is at least correct according to the protocol.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I've added the no-store directive and left the no-cache for now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
created #27 for the full no-cache support
'respect_cache_headers' => true, | ||
'hash_algo' => 'sha1', | ||
'methods' => ['GET', 'HEAD'], | ||
'respect_response_cache_directives' => ['no-cache', 'private', 'max-age', 'no-store'], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like adding no-cache is a change over current behaviour. i think we can consider that a bugfix. but please add a note to the changelog so people have a chance of seeing the change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added a line about this change in the changelog!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks! almost there. i noticed one glitch in the constructor sanity check.
src/CachePlugin.php
Outdated
* } | ||
*/ | ||
public function __construct(CacheItemPoolInterface $pool, StreamFactory $streamFactory, array $config = []) | ||
{ | ||
$this->pool = $pool; | ||
$this->streamFactory = $streamFactory; | ||
|
||
if (isset($config['respect_cache_headers']) && $config['respect_response_cache_directives']) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the second check also needs to have an isset. as is, this will be a fatal error if respect_response_cache_directives is not set. (and throw no exception if respect_cache_headers is set and the other is []
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good catch! Fixed!
2cccec7
to
42613cc
Compare
Fixed comment from @dbu and squashed/rebase the PR |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks good to me now. what do the other maintainers think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice. I really like this PR now =)
Thank you @acrobat
styleci is a bit weird. i guess we should remove that dot (i would be ok with it, but styleci should be green and i guess we can't easily make it allow just that one place) |
42613cc
to
1568bea
Compare
@dbu Yeah it was indeed a bit weird that's why I left it there. But removed the dot and now is styleci passing again! |
thanks a lot! created php-http/documentation#178 about the documentation. needs not a lot, but would be great to have it mentioned in the doc |
Yes indeed, that was still a todo for after the PR was merged. I will try to do that tonight! |
Is there an ETA for the 1.3.0 release? As I would like to implement this feature in KnpLabs/php-github-api |
There is no ETA yet. But I guess it will be sooner rather than later. Feel free to start a PR on KnpLabs/php-github-api and I'll be happy to review that one. I'll make sure both libraries release a new tag with this feature. |
Thanks for the info @Nyholm! I will start a PR to change the cachePlugin usage |
@sagikazarmark Do you maybe have some time to create a new release? Would be awesome to use this feature and the method caching feature from #24! Thanks! |
Wait a bit with a release. I just noticed that the regexp in #24 allowes |
ping @tuupola any time to fix this so we can have a release anytime soon? |
Sorry totally missed this one. My mailbox is quite full lately. @tuupola ping me if you are done or need anything from me. |
Sorry for the delay. I will do it today. |
This is a first simple version to get the discussion going and see what changes are needed here.
What's in this PR?
As discussed in #3 this PR introduces 2 factory methods and an option to specify which cache control directives should be checked
Checklist